/*
 * @(#)HtmlParserHypertextUnitBuilder.java	0.1 03/11/05
 *
 * Copyright 2004 Fabio Gasparetti. All rights reserved.
 */

package org.tabularium.text.html;

import java.io.*;
import java.util.*;

import org.htmlparser.*;
import org.htmlparser.lexer.*;
import org.htmlparser.tags.*;
import org.htmlparser.visitors.*;

import org.tabularium.net.UrlNormalizer;

/**
 * An implementation of <code>HypertextUnitBuilder</code> based on the
 * <tt>htmlparser</tt> library.
 * <p>
 * See {@link http://htmlparser.sourceforge.net/}
 * 
 * @author Fabio Gasparetti
 * @version 0.1 03/11/05
 */

public class HtmlParserHypertextUnitBuilder extends HypertextUnitBuilder {
	protected HypertextUnitFactory unitFactory = null;

	public HtmlParserHypertextUnitBuilder() {
		super();
	}

	/*
	 * (non-Javadoc)
	 * @see org.tabularium.text.html.HypertextUnitBuilder#build(java.lang.String, java.lang.String, org.tabularium.text.html.HypertextUnitFactory)
	 */
	public HypertextUnit build(String url, String htmltext,
			HypertextUnitFactory unitFactory) throws Exception {
		this.unitFactory = unitFactory;
		Lexer lexer;
		org.htmlparser.Parser parser;

		lexer = new org.htmlparser.lexer.Lexer(htmltext);
		parser = new org.htmlparser.Parser(lexer);
		UnitVisitor visitor = new UnitVisitor(url, super.htmlDelim,
				super.htmlUnitDelim, super.htmlUnitDblDelim, unitFactory);
		parser.visitAllNodesWith(visitor);
		HypertextUnit root = visitor.getRoot();
		root = assignIds(root);
		return root;
	}

	// for debugging
	public static void main(String[] args) throws Exception {
		
		InputStream is = new FileInputStream("prova2.html");
		BufferedReader reader = new BufferedReader(
				new InputStreamReader(is));

		String line;
		StringBuffer doc = new StringBuffer();
		while ((line = reader.readLine()) != null) {
			doc.append(line + "\n");
		}
		is.close();
		HtmlParserHypertextUnitBuilder hu = new HtmlParserHypertextUnitBuilder();
		HypertextUnit root = hu.build("http://www.cnn.com/", doc
				.toString(), new DefaultHypertextUnitFactory());
		if (root != null)
			System.out.println("root:" + root.toIndentString());

		
		/*
		try {
			String virtPage = new String("<html><head><title>TITLE</title></head><body><table><tr><td><a href=\"http://pippo.it\">TITLE</a>DESCR</td></tr></table></body></html>");
			HtmlParserHypertextUnitBuilder hu = new HtmlParserHypertextUnitBuilder();
			HypertextUnit root = hu.build("http://www.cnn.com/",
					virtPage, new DefaultHypertextUnitFactory());
			//if (root != null)
			System.out.println("root:" + root.toIndentString());
			//System.out.println();
			//System.out.println();
			//System.exit(0);
			//System.out.println(root.toCommentedHTML());

		} catch (Exception ex) {
			System.out.println(ex.toString());
			ex.printStackTrace();
		} */
	}
	
}

/* HtmlParser visitor for link windows extraction. See extractLinkWindows(). */
class UnitVisitor extends NodeVisitor {
	protected ArrayList list = new ArrayList();

	protected StringBuffer textBeforeLink = null;

	protected StringBuffer textAfterLink = null;

	protected StringBuffer linkText = new StringBuffer();

	protected StringBuffer buffer = new StringBuffer();

	protected String url = null;

	protected String baseHref = null;

	protected ArrayList stack = new ArrayList(); // stack of HypertextUnit

	// objects

	// protected Logger logger =
	// Logger.getLogger("org.tabularium.um.sam.ContentExtractor.UnitVisitor");

	protected HashSet htmlDelim = new HashSet();

	protected HashSet htmlUnitDelim = new HashSet();

	protected HashSet htmlUnitDblDelim = new HashSet();

	HypertextUnit currentUnit = null;

	HypertextUnit root = null;
	
	protected HypertextUnitFactory unitFactory = null;

	public UnitVisitor(String url, String[] htmlDelim, String[] htmlUnitDelim,
			String[] htmlUnitDblDelim, HypertextUnitFactory unitFactory) {
		super(true);
		this.unitFactory = unitFactory;
		baseHref = url;
		this.url = url;

		for (int i = 0; i < htmlDelim.length; i++) {
			this.htmlDelim.add(htmlDelim[i]);
		}
		for (int i = 0; i < htmlUnitDelim.length; i++) {
			this.htmlUnitDelim.add(htmlUnitDelim[i]);
		}
		for (int i = 0; i < htmlUnitDblDelim.length; i++) {
			this.htmlUnitDblDelim.add(htmlUnitDblDelim[i]);
		}
		// root unit
		root = unitFactory.newHypertextUnit();
		root.type = "ROOT";
		root.parent = null;
		currentUnit = root;
	}

	public HypertextUnit getRoot() {
		return currentUnit;
	}

	public void visitTag(Tag tag) {
		if (tag.breaksFlow()) {
			buffer.append(" ");
		}
		if (tag instanceof BaseHrefTag) {
			BaseHrefTag baseHrefTag = (BaseHrefTag) tag;
			baseHref = baseHrefTag.getBaseUrl();
			if (baseHref == null) {
				baseHref = url;
			}
		} else if (tag instanceof TitleTag) {
			HypertextUnit unit = unitFactory.newHypertextUnit();
			unit.type = "TITLE";
			unit.text = ((TitleTag) tag).getTitle();
			unit.attributes = UnitVisitor.extractAttributes(tag);
			unit.parent = currentUnit;
			currentUnit.content.add(unit);
		} else if (tag instanceof LinkTag) {
			storeTextUnit();
			LinkTag linkTag = (LinkTag) tag;
			HypertextUnit unit = unitFactory.newHypertextUnit();
			unit.type = "LINK";
			String u = UrlNormalizer.unrelativizeUrl(baseHref, linkTag
					.getLink());
			unit.url = u; // could be null if unrelativization fails
			unit.attributes = UnitVisitor.extractAttributes(tag);
			unit.parent = currentUnit;
			unit.text = linkTag.getAttribute("TITLE");
			
			unit.parent = currentUnit;
			// push
			currentUnit.content.add(unit);
			stack.add(currentUnit);
			currentUnit = unit;
		} else if (tag instanceof ImageTag) {
			storeTextUnit(); // store text seen until the image
			// adds the alt-attribute text of the images
			HypertextUnit unit = unitFactory.newHypertextUnit();
			ImageTag imageTag = (ImageTag) tag;
			unit.text = imageTag.getAttribute("ALT"); // could be null
			unit.type = "IMG";
			String u = UrlNormalizer.unrelativizeUrl(baseHref, imageTag
					.getImageURL());
			unit.url = u;
			unit.attributes = UnitVisitor.extractAttributes(tag);
			unit.parent = currentUnit;
			currentUnit.content.add(unit);
		}

		if (htmlUnitDblDelim.contains(tag.getTagName())) {
			storeTextUnit();
			// create a new unit
			HypertextUnit unit = unitFactory.newHypertextUnit();
			unit.type = tag.getTagName();
			unit.parent = currentUnit;
			unit.attributes = UnitVisitor.extractAttributes(tag);
			// push
			currentUnit.content.add(unit);
			stack.add(currentUnit);
			currentUnit = unit;
		}
		/* some tags have optional closing elements */
		else if (htmlUnitDelim.contains(tag.getTagName())) {
			// create a new delim unit and attach it
			storeTextUnit();
			// add the delim
			HypertextUnit unit = unitFactory.newHypertextUnit();
			unit.type = tag.getTagName();
			unit.parent = currentUnit;
			unit.attributes = UnitVisitor.extractAttributes(tag);
			currentUnit.content.add(unit);
			stack.add(currentUnit);
			currentUnit = unit;
		} else if (htmlDelim.contains(tag.getTagName())) {
			// add the delim unit to the parent's array
			storeTextUnit();
			// add the delim
			HypertextUnit unit = unitFactory.newHypertextUnit();
			unit.type = tag.getTagName();
			unit.parent = currentUnit;
			unit.attributes = UnitVisitor.extractAttributes(tag);
			currentUnit.content.add(unit);
		}
	}
	
	public static HypertextUnitAttribute[] extractAttributes(Tag t) {
		HypertextUnitAttribute[] attributes = null;
		Vector v = t.getAttributesEx();
		if (v != null && v.size() > 0) {
			ArrayList aa = new ArrayList();
			for (int i = 0; i < v.size(); i++) {
				HypertextUnitAttribute a = new HypertextUnitAttribute();
				if (((Attribute)v.elementAt(i)).getName() == null)
					continue;
				a.name = ((Attribute)v.elementAt(i)).getName();
				a.value = ((Attribute)v.elementAt(i)).getRawValue();
				aa.add(a);
			}
			attributes = (HypertextUnitAttribute[])aa.toArray(new HypertextUnitAttribute[] {});
		} 
		return attributes;
	}

	public void visitEndTag(Tag endTag) {
		/*
		 * Stores the current node if: 1) the corresponding closing element is
		 * found (for htmlUnitDblDelim tags) 2) it is a htmlUnitDelim (doesn't
		 * have a mandatory closing element), and the closing parent of the
		 * parent is found 3) if the </A> element is found
		 */
		if (endTag.getTagName().equals(currentUnit.type)
				|| (htmlUnitDelim.contains(currentUnit.type) && endTag
						.getTagName().equals(currentUnit.parent.type))
				|| (endTag.getTagName().equals("A") && currentUnit.type
						.equals("LINK"))) {
			storeTextUnit();
			// pop parent
			HypertextUnit unit = (HypertextUnit) stack.remove(stack.size() - 1);
			currentUnit = unit;
		}
	}

	public void visitStringNode(Text stringNode) {
		try {
			String s = stringNode.toPlainTextString();
			Node parent = stringNode.getParent();
			// avoid script code
			if ((parent != null) && (parent instanceof ScriptTag))
				return;
			buffer.append(s + " ");
		} catch (Exception ex) {
		}
	}

	public void finishedParsing() {
		storeTextUnit();
	}

	// collect last text and assign it to the last unit
	protected void storeTextUnit() {
		// collect last text and assign it to the last unit
		String text = buffer.toString().trim();
		if (text.length() > 0) {
			// tokenize
			HypertextUnit u = unitFactory.newHypertextUnit();
			u.text = text;
			u.type = "TEXT";
			u.parent = currentUnit;
			currentUnit.content.add(u);
			buffer = new StringBuffer();
		}
	}

	// debug
	private void dumpStack() {
		System.out.print("STACK: ");
		Iterator iter = stack.iterator();
		while (iter.hasNext()) {
			HypertextUnit u1 = (HypertextUnit) iter.next();
			System.out.print(u1.type + " ");
		}
		System.out.println();
	}
	
}